package main

import (
	"fmt"
	"time"
)

//chanel是线程安全的

func channelTest() {
	//创建一个可以存放3个int类型的通道
	var intChan chan int
	intChan = make(chan int, 3)
	//查看intChan是什么
	fmt.Printf("intChan value is %v \t intChan originally addr is %p \n", intChan, &intChan)
	//向管道写入数据
	//注意点，当我们向管道写入数据时，数据长度不能超过cap的容量
	intChan <- 10
	intChan <- 100
	intChan <- 18
	//查看管道的长度，和容量
	fmt.Printf("channel len is %v , cap is %v \n", len(intChan), cap(intChan)) //channel len is 3 , cap is 3
	//从管道中取数据
	num1 := <-intChan
	num2 := <-intChan
	num3 := <-intChan
	fmt.Printf("num1 is %d,num2 is %d,num3 is %d \n", num1, num2, num3)        //num1 is 10,num2 is 100,num3 is 18
	fmt.Printf("channel len is %v , cap is %v \n", len(intChan), cap(intChan)) //channel len is 0 , cap is 3
	num4 := <-intChan                                                          //异常
	//fatal error: all goroutines are asleep - deadlock!
	//goroutine 1 [chan receive]:
	fmt.Printf("num4 is %d", num4)
}

func channelTestMap() {

	var mapChannel chan map[string]string
	mapChannel = make(chan map[string]string, 10)

	m1 := make(map[string]string, 20)
	m1["city01"] = "天津"
	m1["city02"] = "上海"

	m2 := make(map[string]string, 20)
	m2["hero01"] = "宋江"
	m2["hero02"] = "武松"

	mapChannel <- m1
	mapChannel <- m2

	fmt.Printf("channel len is %v , cap is %v \n", len(mapChannel), cap(mapChannel))
}

type cat struct {
	Name string
	Age  int
}

func channelTestInterface() {
	var interfaceChannel chan interface{}
	interfaceChannel = make(chan interface{}, 10)

	interfaceChannel <- cat{Name: "tom", Age: 18}
	interfaceChannel <- cat{Name: "jin", Age: 17}
	interfaceChannel <- 100
	interfaceChannel <- "hell channel"

	cat01 := <-interfaceChannel
	fmt.Printf("cat01 type is %T,and value is %v \n", cat01, cat01) //cat01 type is main.cat,and value is {tom 18}
	newCat := cat01.(cat)
	fmt.Printf("newCat name is %v ,and age is %d \n", newCat.Name, newCat.Age)                   //newCat name is tom ,and age is 18
	fmt.Printf("channel len is %v , cap is %v \n", len(interfaceChannel), cap(interfaceChannel)) //channel len is 3 , cap is 10
}

/**
  1.channel 关闭后，就不能向channel写数据了，但可以读数据
  2.channel 支持for-range的方式进行遍历，遍历之前，必须关闭channel,否则出现 deadlock错误，遍历完后，就会退出遍历

*/
func channelTestForRange() {
	var interfaceChannel chan interface{}
	interfaceChannel = make(chan interface{}, 10)

	interfaceChannel <- cat{Name: "tom", Age: 18}
	interfaceChannel <- cat{Name: "jin", Age: 17}
	interfaceChannel <- 100
	interfaceChannel <- "hell channel"

	close(interfaceChannel) //关闭channel
	//interfaceChannel <-1001  //错误：panic: send on closed channel

	//读正常
	cat01 := <-interfaceChannel
	fmt.Printf("cat01 type is %T,and value is %v \n", cat01, cat01) //cat01 type is main.cat,and value is {tom 18}
	newCat := cat01.(cat)
	fmt.Printf("newCat name is %v ,and age is %d \n", newCat.Name, newCat.Age) //newCat name is tom ,and age is 18

	//遍历channel
	for value := range interfaceChannel {
		fmt.Printf("value is %v \n", value)
	}

}

/**
character-1
   1. channel 可以设置只读，或者只写
   2. 默认是双向的
*/
func testChannelCharacter() {
	//只写
	var chan2 chan<- int
	chan2 = make(chan int, 3)

	chan2 <- 10
	chan2 <- 100
	chan2 <- 1000
	//编译失败
	//value01 := <-chan2

	//只读
	var chan3 <-chan int
	chan3 = make(chan int, 3)
	//编译失败
	//chan3 <- 30
	num01 := <-chan3
	fmt.Printf("num01 is %d \n", num01)

}
func testChannelCharacter2() {
	chan03 := make(chan int, 100)
	onlyWrite(chan03)
	onlyRead(chan03)
}

//re 只读
func onlyRead(rd <-chan int) {
	for value := range rd {
		fmt.Printf("value is %d \n", value)
	}
	//编译失败
	//rd <- 52
}

func onlyWrite(wt chan<- int) {
	for i := 0; i < 100; i++ {
		wt <- i
	}

	//编译失败
	//num := <-wt
	//fmt.Printf("num is %d \n",num)
	close(wt)
}

/**
character-2
 使用select 可以解决从管道取数据的阻塞问题
*/
func testChannelCharacter3() {
	chanInt := make(chan int, 10)

	for i := 0; i < 10; i++ {
		chanInt <- i
	}

	chanString := make(chan string, 10)

	for i := 0; i < 10; i++ {
		chanString <- fmt.Sprintf("hello %d", i)
	}

	//传统的方法在遍历管道时，如果不关闭会阻塞而导致 deadlock
	//问题，在实际开发中，可能我们不好确定什么时候关闭管道
	//可以使用select 方法可以解决问题
	for {
		select {
		//这里，如果chanInt一直没有关闭，不会一直阻塞而deadlock,会自动到下一个case匹配
		case value := <-chanInt:
			fmt.Printf("从chanInt 读取的数据 %d \n", value)
		case value := <-chanString:
			fmt.Printf("从 chanString 读到数据 %s \n", value)
		default:
			fmt.Printf("都不取不到数据，不玩了 ,当前时间 %v \n", time.Now().Format("2006-01-02 15:04:05"))
			time.Sleep(time.Millisecond * 1000)

		}
	}

}

/**
  character-3
  goroutine 中使用recover,解决协程中出现panic，导致程序崩溃问题；
  如果我们起了一个协程，但是这个协程出现了panic，如果我们没有捕获这个panic,就会造成整个程序崩溃，这时我们可以在goroutine中使用recovery来捕获panic，进行处理，这样即使这个
  协程发生的问题，但是主线程仍然不受影响，可以继续执行；
*/

func testChannelCharacter4() {
	go correctFunc()
	go errorFunc()

	for i := 0; i < 10; i++ {
		fmt.Printf("当前时间值： %d \n", time.Now().Unix())
		time.Sleep(time.Second)
	}
}
func correctFunc() {
	for i := 1; i < 10; i++ {
		time.Sleep(time.Second)
		fmt.Printf("hello world \n")
	}
}

func errorFunc() {
	//这里可以使用defer + recover 进行异常处理
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("errorFunc() 方法发生异常，err: %v", err)
		}
	}()

	var myMap map[string]interface{}
	myMap["name"] = "jansom" //错误：panic: assignment to entry in nil map
	myMap["age"] = 18
}

func main() {

	//testChannelCharacter3()
	testChannelCharacter4()
}
